﻿#define _CRT_SECURE_NO_WARNINGS 1

#include"ThreadCache.h"
#include"CentralCache.h"

// 申请内存对象 
void* ThreadCache::Allocate(size_t size)
{
	assert(size <= MAX_BYTES);
	//对齐的内存大小
	size_t alignSize = SizeClass::RoundUp(size);
	//映射的几号桶
	size_t index = SizeClass::Index(size);

	if (!_freeLists[index].Empty())
	{
		return _freeLists[index].Pop();
	}
	else
	{	// 从中⼼缓存获取对象				给对齐好的大小，下一层就不用考虑对齐
		return FetchFromCentralCache(index, alignSize);
	}
}

//释放内存对象
void ThreadCache::Deallocate(void* ptr, size_t size)
{
	assert(ptr);
	assert(size <= MAX_BYTES);

	//找对映射的自由链表桶，对象头插进去
	size_t index = SizeClass::Index(size);
	_freeLists[index].Push(ptr);

	//当链表长度大于一次批量申请的内存时，就开始还一段list给central cache
	if (_freeLists[index].Size() >= _freeLists[index].MaxSize())
	{
		ListTooLong(_freeLists[index], size);
	}
}

// 释放对象时，链表过⻓时，回收内存回到中⼼缓存 
void ThreadCache::ListTooLong(FreeList& list, size_t size)
{//回收一个批量的内存
	void* start = nullptr;
	void* end = nullptr;
	list.PopRange(start, end, list.MaxSize());//从自由链表中断链

	// 将⼀定数量的对象释放到span跨度
	CentralCache::GetInstance()->ReleaseListToSpans(start, size);//这个函数不需要end,遍历到空就行
}


// 从中心缓存获取对象					//映射的几号桶	对齐后内存大小
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	//慢开始反馈调节算法	小内存给多一点(上限锁死)，大内存给少一点(下限锁死)
	//size_t batchNum = SizeClass::NumMoveSize(size);
	//1.最开始不会向central cache一次批量要太多，因为要太多了可能用不完
	//2.如果你不断有size大小内存需求，即一个桶多次申请内存，batchNum就会不断增长，直到上限
	//3.size越大，一次向central cache要的batchNum就越小(毕竟要的内存大，负载高一点)
	//4.size越小，一次向central cache要的batchNum就越大
	size_t batchNum = min(_freeLists[index].MaxSize(), SizeClass::NumMoveSize(size));
			//批量		//选择MaxSize和算法给出的小的那个		故NumMoveSize锁死上限
					//min在algorithm是函数，在windows.h是宏，这里用std::min会报错，宏覆盖了标准库的min

	if (_freeLists[index].MaxSize() == batchNum)
	{
		_freeLists[index].MaxSize() += 1;//每申请一次，MaxSize＋1
	}

	void* start = nullptr;	//申请内存块的起始地址
	void* end = nullptr;	//最后一个内存块的起始地址
	//实际获取申请到数量	如果一个span里没有那么多内存块，能给多少给多少
	size_t actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
	assert(actualNum >= 1);//至少拿一个

	if (actualNum == 1)
	{
		assert(start == end);
		return start;		//start返回给用户，不进入threadcache自由链表
	}
	else
	{//申请到多份内存块				//把start内存块的下一个内存块传入函数
		_freeLists[index].PushRange(NextObj(start), end, actualNum - 1);
		return start;		//start返回给用户，不进入threadcache自由链表
							//start是用户申请的内存，(NextObj(start), end)是从centralcache额外申请的内存
							//需要链入threadcache自由链表
	}
}